package org.mentalsmash.crossroads.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mentalsmash.crossroads.Configuration;


/**
 * 
 * @author Andrea Reale
 * Performs Web Searches using Google AJAX API.
 * For a more detailed explanation on how the search is performed see at http://code.google.com/intl/it-IT/apis/ajaxsearch/documentation
 * @see http://code.google.com/intl/it-IT/apis/ajaxsearch/documentation
 */
public class WebSearcher {
	

	// The Google Web Search API Key used to perform searches
	private static Logger log = Logger.getLogger(WebSearcher.class);
	private final static String ENCODING = "utf-8";
	
	//Number of results returned:
	//	Possible values:
	//		small: 4 results
	//		large: 8 results
	private String _resultSize = Configuration.getInstance().isLargeGoogleSearch() ? "large" : "small";
	
	//language code for setting the language for doing web searchs
	private String _langCode = Configuration.getInstance().getLanguage();
	
	/**
	 * Constructs a "default" Web searcher. 
	 * Google Api key, domain reference, search language and result size are set to default values.
	 * DEFAULT_RES_SIZE = large
	 * DEFAULT_LANG = en
	 */
	public WebSearcher()
	{
		
	}
	
	/**
	 * Builds a web searcher, given a language code (e.g. the string "en") and a boolean telling whether the result size must be small (i.e. 4 results)
	 * Note that Google API KEy and reference domain will be initialized to a default value
	 * @param lang a two characters code representing the language (e.g. 'it' or 'en')
	 * @param smallResultSize a boolean telling wether the result size must be small (4 results) or large (8 results)
	 */
	public WebSearcher(String lang, boolean smallResultSize)
	{
		if(smallResultSize)
			_resultSize = "small";
		_langCode = lang;
	}
	
	/**
	 * Builds a WebSearcher.
	 * For accurate details about Google API Key and reference Domain URL see at http://code.google.com/intl/it-IT/apis/ajaxsearch/documentation
	 * @param googleApiKey a string key provided by google
	 * @param refURL the domain associated to the given key. It should be in the form of an url
	 * @param lang the two characters language code to perfom the search
	 * @param smallResSize true if you want a small result set (only 4 results) instead of a large one (8 results)
	 */
	public WebSearcher(String googleApiKey, String refURL, String lang, boolean smallResSize){
		
	}
	
	/**
	 * Method for doing the actual search using google AJAX api
	 * See details at http://code.google.com/intl/it-IT/apis/ajaxsearch/documentation
	 * @param escapedQuery the query to submit in an escaped form (e.g. ' ' is '%20' )
	 * @return an array of WebSearchResults. The size of this arrays depends upon the ResultSize property
	 */
	protected WebSearchResult[] doSearch(String query){
		String escapedQuery = null;
		try {
			escapedQuery = URLEncoder.encode(query,ENCODING);
		} catch (UnsupportedEncodingException e1) {
			log.fatal("Unsupported encoding for requested query",e1);
			e1.printStackTrace();
			System.exit(-5);
		}
		
		StringBuilder builder = null;
		
		
		try{
			log.debug("Querying google for results.. query is " + escapedQuery);
			//building the request for google
			// see http://code.google.com/intl/it-IT/apis/ajaxsearch/documentation/reference.html#_restUrlBase for details
			URL url = new URL("http://ajax.googleapis.com/ajax/services/search/web?v=1.0" + 
					"&q=" + escapedQuery + 
					"&key="+ Configuration.getInstance().getGoogleApiKey() + 
					"&hl=" + _langCode +
					"&lr=" + "lang_" + _langCode +
					"&rsz=" + _resultSize);
			URLConnection connection = url.openConnection();
			connection.addRequestProperty("Referer", Configuration.getInstance().getReferenceDomain());

			String line;
			builder = new StringBuilder();
			BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			while((line = reader.readLine()) != null) {
				builder.append(line);
			}
		}
		catch (MalformedURLException e)
		{
			log.warn("unable to connect to google",e);
			e.printStackTrace();
		}catch(IOException e ){
			log.warn("unable to get results from google",e);
			e.printStackTrace();

		}

		String results = builder.toString();
		
		JSONObject json = null;
		
		try {
			json = new JSONObject(results);	
		} catch (JSONException e) {
			e.printStackTrace();
			System.exit(-5);
		}
		
		WebSearchResult[] resls =  jsonResToWebSearchRes(json);
		log.debug("Search completed. Returned " +resls.length + " results.");
		
		return resls;
	}
	
	/**
	 * Performs conversion from JSONObject results (as returned by google) and WebSearchResult format
	 * Too see how google returns his results, please look at http://code.google.com/intl/it-IT/apis/ajaxsearch/documentation
	 * @param jsonObj An object representing the search result in json format
	 * @return an array containing a WebSearchResult instance for each result contained in the JSONObject
	 */
	private WebSearchResult[] jsonResToWebSearchRes(JSONObject jsonObj)
	{
		WebSearchResult[] retArray = null;
		
		try {
			
			JSONArray resArray = jsonObj.getJSONObject("responseData").getJSONArray("results");
			
			int resLength = resArray.length();
			retArray = new WebSearchResult[resLength];
			
			//Populating Google Web Search results array
			for(int i=0; i<resLength;i++){
				String turl = resArray.getJSONObject(i).getString("url");
				String title = resArray.getJSONObject(i).getString("titleNoFormatting");
				URL url = new URL(turl);
				retArray[i] = new WebSearchResult(title,url);
			}
			
		} catch (JSONException e) {
			e.printStackTrace();
			System.exit(-5);
		} catch(MalformedURLException e)
		{
			e.printStackTrace();
			System.err.println("Malformed Url in google result");
			System.exit(-5);
		}
		
		return retArray;
	}
	
	public static void main(String[] args){
		new WebSearcher().doSearch("Ferrari");
	}

}
